﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace Dungeon_Dive
{
    /// <summary>
    /// Console for printing gamestate information and issuing commands.
    /// </summary>
    class Console : DrawableGameComponent
    {
        Game1 parentGame;
        Dictionary<Keys, bool> keysDown;
        XMLEngine XmlEngine;
        PythonEngine pyEngine;
        Log log;
        SpriteBatch spriteBatch;
        Random random;

        public SpriteFont Font { get; set; }
        public List<String> Text { get; set; }
        public int MaxTextRows { get; set; }
        public LogicEngine Level { get; set; }

        public Console(Game1 game) : base(game)
        {
            parentGame = game;
            Text = new List<string>();
            Text.Add(""); //First line of text
            MaxTextRows = 20;
            Font = null;
            keysDown = new Dictionary<Keys,bool>();
            XmlEngine = (XMLEngine)parentGame.Services.GetService(typeof(XMLEngine));
            pyEngine = (PythonEngine)parentGame.Services.GetService(typeof(PythonEngine));
            random = new Random();
        }

        public override void Update(GameTime gameTime)
        {
            while (Text.Count() > MaxTextRows)
            {
                Text.RemoveAt(0);
            }

            foreach (Keys key in Keyboard.GetState().GetPressedKeys())
            {
                keysDown[key] = true;
            }

            //Originally I attempted to use 'foreach (Keys key in keysDown)', but apparently C# doesn't allow any kind of
            //modification to the container if enumerated in that way, and 'keysDown[key] = false;' counts as a modification,
            //so I had to use a somewhat more "oldschool" method.
            for (int i = 0; i < keysDown.Count; ++i)
            {
                Keys key = keysDown.ElementAt(i).Key;
                if (Keyboard.GetState().IsKeyUp(key) && keysDown[key])
                {
                    
                    keysDown[key] = false;
                    if ((key >= Keys.A && key <= Keys.Z) || (key >= Keys.D0 && key <= Keys.D9))
                    {
                        Text[Text.Count - 1] += key.ToString();
                    }
                    else if (key == Keys.OemPeriod)
                    {
                        Text[Text.Count - 1] += ".";
                    }
                    else if (key == Keys.Space)
                    {
                        Text[Text.Count - 1] += " ";
                    }
                    else if (key == Keys.Enter)
                    {
                        ParseCurrentLine();
                        Text.Add("");
                    }
                    else if (key == Keys.Back)
                    {
                        if (Text[Text.Count - 1].Count() > 0)
                        {
                            Text[Text.Count - 1] = Text[Text.Count - 1].Remove(Text[Text.Count - 1].Length - 1);
                        }
                    }
                }
            }

            base.Update(gameTime);
        }

        public override void Draw(GameTime gameTime)
        {
            if (Font != null)
            {
                spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Deferred, SaveStateMode.SaveState);
                for (int i = 0; i < Text.Count(); ++i)
                {
                    spriteBatch.DrawString(Font, Text.ElementAt(i), new Vector2(10, Font.LineSpacing * i),
                        Color.White, 0f, new Vector2(0,0), 1.0f, SpriteEffects.None, 0);
                }
                spriteBatch.End();
            }
            base.Draw(gameTime);
        }

        public void ParseCurrentLine()
        {
            Text[Text.Count-1] = Text[Text.Count-1].Trim();
            String command = Text[Text.Count - 1].Split(" ".ToCharArray(), 2)[0];
            String parameters = Text[Text.Count -1].TrimStart((command + " ").ToCharArray());
            command = command.ToLower();

            switch (command)
            {
                case "loadxml":
                    Text.Add("Loading file " + parameters + "...");
                    if (XmlEngine.OpenFile(parameters))
                    {
                        Text[Text.Count - 1] += " Success!";
                    }
                    else
                    {
                        Text[Text.Count - 1] += " Failed!";
                    }

                    break;

                case "nextelem":
                    Text.Add(XmlEngine.NextElement());
                    break;

                case "getcontent":
                    Text.Add(XmlEngine.GetContent());
                    break;

                case "getvalue":
                    Text.Add(XmlEngine.GetValue());
                    break;

                case "creategolem":
                    Monster newNPC = new Monster(parentGame, Level, "golem1", 3);
                    for (int x = (int)Level.TPC.TilePosition.X - 5; x < Level.TPC.TilePosition.X + 5; ++x)
                    {
                        for (int z = (int)Level.TPC.TilePosition.Z - 5; z < Level.TPC.TilePosition.Z + 5; ++z)
                        {
                            if (x > 0 && x < 50 && z > 0 && z < 50)
                            {
                                if (Level.Tiles[x, z] != null)
                                {
                                    if (Level.Tiles[x, z].Type == TileTypes.Floor)
                                    {
                                        newNPC.Position = new Vector3(x * 200, 120, z * 200);
                                    }
                                }
                            }
                        }
                    }

                    newNPC.Scale = 0.5f;  // golems are just too big for this world
                    newNPC.CurrentAnim = "walk";
                    newNPC.Speed = 2;
                    newNPC.FrameDelay = TimeSpan.FromMilliseconds(200);
                    newNPC.Play();
                    Level.Born.Add(newNPC);
                    break;

                case "renderbspheres":
                    Level.RenderBoundingSpheres = !Level.RenderBoundingSpheres;
                    break;

                case "deathless":
                    Level.Deathless = !Level.Deathless;
                    break;

                case "createwater":
                    Monster newWaterBlob = new Monster(parentGame, Level, "watere", 3);
                    for (int x = (int)Level.TPC.TilePosition.X - 5; x < Level.TPC.TilePosition.X + 5; ++x)
                    {
                        for (int z = (int)Level.TPC.TilePosition.Z - 5; z < Level.TPC.TilePosition.Z + 5; ++z)
                        {
                            if (x > 0 && x < 50 && z > 0 && z < 50)
                            {
                                if (Level.Tiles[x, z] != null)
                                {
                                    if (Level.Tiles[x, z].Type == TileTypes.Floor)
                                    {
                                        newWaterBlob.Position = new Vector3(x * 200, 2, z * 200);
                                    }
                                }
                            }
                        }
                    }

                    newWaterBlob.Scale = 8f;
                    newWaterBlob.CurrentAnim = "walk";
                    newWaterBlob.Speed = 2;
                    newWaterBlob.FrameDelay = TimeSpan.FromMilliseconds(200);
                    newWaterBlob.Play();
                    Level.Born.Add(newWaterBlob);
                    break;

                case "createfire":
                    Monster newWilloWisp = new Monster(parentGame, Level, "wisp", 3);
                    for (int x = (int)Level.TPC.TilePosition.X - 5; x < Level.TPC.TilePosition.X + 5; ++x)
                    {
                        for (int z = (int)Level.TPC.TilePosition.Z - 5; z < Level.TPC.TilePosition.Z + 5; ++z)
                        {
                            if (x > 0 && x < 50 && z > 0 && z < 50)
                            {
                                if (Level.Tiles[x, z] != null)
                                {
                                    if (Level.Tiles[x, z].Type == TileTypes.Floor)
                                    {
                                        newWilloWisp.Position = new Vector3(x * 200, 2, z * 200);
                                    }
                                }
                            }
                        }
                    }

                    newWilloWisp.Scale = 1.0f;
                    newWilloWisp.CurrentAnim = "walk";
                    newWilloWisp.Speed = 2;
                    newWilloWisp.FrameDelay = TimeSpan.FromMilliseconds(200);
                    newWilloWisp.Play();
                    Level.Born.Add(newWilloWisp);
                    break;

                case "freecamera":
                    Level.Camera.Free = !Level.Camera.Free;
                    break;

                case "fps":
                    parentGame.FPSCounter.Visible = !parentGame.FPSCounter.Visible;
                    break;
            }
        }

        public override void Initialize()
        {
            log = (Log)parentGame.Services.GetService(typeof(Log));
            log.Write("Console.Init()");
            base.Initialize();
        }

        protected override void LoadContent()
        {
            log.Write("Console.LoadContent()");
            ContentManager contentManager = (ContentManager)parentGame.Services.GetService(typeof(ContentManager));
            spriteBatch = contentManager.SpriteBatch;
            base.LoadContent();
        }
    }
}
